/*
 *   Copyright 2012-present OSBI Ltd
 *
 *   Licensed under the Apache License, Version 2.0 (the "License");
 *   you may not use this file except in compliance with the License.
 *   You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *   Unless required by applicable law or agreed to in writing, software
 *   distributed under the License is distributed on an "AS IS" BASIS,
 *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *   See the License for the specific language governing permissions and
 *   limitations under the License.
 */

// Packages
import React, { Component, Fragment } from 'react';
import PropTypes from 'prop-types';
import { findIndex, uniq } from 'lodash';
import classnames from 'classnames';
import {
  Button,
  Callout,
  Classes,
  Dialog,
  FormGroup,
  InputGroup,
  Intent,
  Position
} from '@blueprintjs/core';
import PasswordMask from 'react-password-mask';
import { Select } from 'antd';
import { Form, Validation } from 'calidation';

// Services
import { UserService } from '../../../../../../services';

// Dialogs
import ChangePasswordFormDialog from './ChangePasswordFormDialog';

// Utils
import { Saiku } from '../../../../../../utils';

// Styles
import './UserFormDialog.css';

// Components
const { Option } = Select;

// Constants
const OPTIONS_ROLES = ['ROLE_ADMIN', 'ROLE_USER'];
const FORM_VALIDATION_CONFIG_1 = {
  username: {
    isRequired: 'Username field is required'
  },
  email: {
    isRequired: 'Email field is required',
    isEmail: 'Please enter a valid email address'
  }
};
const FORM_VALIDATION_CONFIG_2 = {
  password: {
    isRequired: 'Password field is required',
    isMinLength: {
      message: 'Password must at least be 4 characters long',
      length: 4
    }
  },
  repeatPassword: {
    isRequired: 'Please fill out the password a second time',
    isEqual: ({ fields }) => ({
      message: 'The two password must match',
      value: fields.password,
      validateIf: fields.password.length > 0
    })
  }
};

class UserFormDialog extends Component {
  state = {
    initialValues: {},
    roles: [],
    formValidationConfig: FORM_VALIDATION_CONFIG_1,
    isOpenChangePasswordFormDialog: false,
    isInvalidUsername: false,
    loading: false
  };

  componentWillMount() {
    const { editMode, userData } = this.props;

    if (!editMode) {
      this.setState({
        formValidationConfig: {
          ...FORM_VALIDATION_CONFIG_1,
          ...FORM_VALIDATION_CONFIG_2
        }
      });
    } else {
      const { username, email, roles } = userData;

      this.setState({ initialValues: { username, email }, roles: uniq(roles) });
    }
  }

  handleChangePasswordFormDialog = () => {
    this.setState(prevState => ({
      isOpenChangePasswordFormDialog: !prevState.isOpenChangePasswordFormDialog
    }));
  };

  handleChangeRoles = selectedItems => {
    this.setState({ roles: selectedItems });
  };

  handleSubmit = ({ fields, errors, isValid }) => {
    if (isValid) {
      const { editMode, userData, getUsers, onClose } = this.props;
      const { roles } = this.state;
      const { username, email, password } = fields;
      let data = {};

      this.setState({ loading: true });

      UserService.getUsers()
        .then(res => {
          if (res.status === 200) {
            const userIndex = findIndex(res.data, { username });

            if (
              (!editMode && userIndex !== -1) ||
              (editMode && userIndex !== -1 && username !== userData.username)
            ) {
              this.setState({
                isInvalidUsername: true,
                loading: false
              });
            } else {
              if (!editMode) {
                data = {
                  username,
                  email,
                  password,
                  roles
                };

                UserService.addUser(data)
                  .then(res => {
                    this.setState({ loading: false });

                    if (res.status === 200) {
                      Saiku.toasts(Position.TOP_RIGHT).show({
                        icon: 'tick',
                        intent: Intent.SUCCESS,
                        message: 'User added'
                      });

                      getUsers();
                      onClose();
                    } else {
                      Saiku.toasts(Position.TOP_RIGHT).show({
                        icon: 'error',
                        intent: Intent.DANGER,
                        message: res.statusText
                      });
                    }
                  })
                  .catch(error => {
                    Saiku.toasts(Position.TOP_RIGHT).show({
                      icon: 'error',
                      intent: Intent.DANGER,
                      message: 'Something went wrong'
                    });
                  });
              } else {
                const { id } = userData;
                data = {
                  id,
                  username,
                  email,
                  password: null,
                  roles
                };

                UserService.updateUser(id, data)
                  .then(res => {
                    this.setState({ loading: false });

                    if (res.status === 200) {
                      Saiku.toasts(Position.TOP_RIGHT).show({
                        icon: 'tick',
                        intent: Intent.SUCCESS,
                        message: 'User updated'
                      });

                      getUsers();
                      onClose();
                    } else {
                      Saiku.toasts(Position.TOP_RIGHT).show({
                        icon: 'error',
                        intent: Intent.DANGER,
                        message: res.statusText
                      });
                    }
                  })
                  .catch(error => {
                    Saiku.toasts(Position.TOP_RIGHT).show({
                      icon: 'error',
                      intent: Intent.DANGER,
                      message: 'Something went wrong'
                    });
                  });
              }
            }
          } else {
            this.setState({ loading: false });
            Saiku.toasts(Position.TOP_RIGHT).show({
              icon: 'error',
              intent: Intent.DANGER,
              message: 'Something went wrong'
            });
          }
        })
        .catch(error => {
          this.setState({ loading: false });
          Saiku.toasts(Position.TOP_RIGHT).show({
            icon: 'error',
            intent: Intent.DANGER,
            message: 'Something went wrong'
          });
        });
    }
  };

  renderErrorInvalidUsername() {
    return (
      <Callout
        className="m-b-20"
        intent={Intent.DANGER}
        style={{ color: '#ee5342' }}
      >
        The username already exists! Enter another name...
      </Callout>
    );
  }

  renderForm() {
    const { editMode, onClose } = this.props;
    const {
      initialValues,
      roles,
      formValidationConfig,
      isInvalidUsername,
      loading
    } = this.state;

    return (
      <Form onSubmit={this.handleSubmit} style={{ margin: 0 }}>
        <Validation config={formValidationConfig} initialValues={initialValues}>
          {({ fields, errors, submitted }) => (
            <Fragment>
              <div className={Classes.DIALOG_BODY}>
                {isInvalidUsername && this.renderErrorInvalidUsername()}

                <FormGroup
                  label="Username"
                  labelFor="username"
                  intent={
                    submitted && errors.username ? Intent.DANGER : Intent.NONE
                  }
                  helperText={
                    submitted && errors.username ? errors.username : ''
                  }
                >
                  <InputGroup
                    name="username"
                    intent={
                      submitted && errors.username ? Intent.DANGER : Intent.NONE
                    }
                    defaultValue={fields.username}
                    autoFocus={!editMode}
                  />
                </FormGroup>
                <FormGroup
                  label="Email"
                  labelFor="email"
                  intent={
                    submitted && errors.email ? Intent.DANGER : Intent.NONE
                  }
                  helperText={submitted && errors.email ? errors.email : ''}
                >
                  <InputGroup
                    name="email"
                    intent={
                      submitted && errors.email ? Intent.DANGER : Intent.NONE
                    }
                    defaultValue={fields.email}
                  />
                </FormGroup>

                {!editMode ? (
                  <Fragment>
                    <FormGroup
                      label="Password"
                      labelFor="password"
                      intent={
                        submitted && errors.password
                          ? Intent.DANGER
                          : Intent.NONE
                      }
                      helperText={
                        submitted && errors.password ? errors.password : ''
                      }
                    >
                      <PasswordMask
                        id="password"
                        name="password"
                        inputClassName={classnames('bp3-input', {
                          'bp3-intent-danger': submitted && errors.password
                        })}
                        value={fields.password}
                      />
                    </FormGroup>
                    <FormGroup
                      label="Repeat Password"
                      labelFor="repeatPassword"
                      intent={
                        submitted && errors.repeatPassword
                          ? Intent.DANGER
                          : Intent.NONE
                      }
                      helperText={
                        submitted && errors.repeatPassword
                          ? errors.repeatPassword
                          : ''
                      }
                    >
                      <PasswordMask
                        id="repeatPassword"
                        name="repeatPassword"
                        inputClassName={classnames('bp3-input', {
                          'bp3-intent-danger':
                            submitted && errors.repeatPassword
                        })}
                        value={fields.repeatPassword}
                      />
                    </FormGroup>
                  </Fragment>
                ) : (
                  <Button
                    className="m-b-10"
                    icon="edit"
                    text="Change Password"
                    onClick={this.handleChangePasswordFormDialog}
                    fill
                  />
                )}

                <FormGroup label="Roles" labelFor="roles">
                  <Select
                    name="roles"
                    mode="tags"
                    value={roles}
                    onChange={this.handleChangeRoles}
                    allowClear
                  >
                    {OPTIONS_ROLES.map(role => (
                      <Option key={role} value={role}>
                        {role}
                      </Option>
                    ))}
                  </Select>
                </FormGroup>
              </div>
              <div className={Classes.DIALOG_FOOTER}>
                <div className={Classes.DIALOG_FOOTER_ACTIONS}>
                  <Button
                    type="submit"
                    text={!editMode ? 'Add' : 'Save'}
                    intent={Intent.DANGER}
                    loading={loading}
                  />
                  <Button text="Close" onClick={onClose} />
                </div>
              </div>
            </Fragment>
          )}
        </Validation>
      </Form>
    );
  }

  render() {
    const { editMode, userData, onClose } = this.props;
    const { isOpenChangePasswordFormDialog } = this.state;

    return (
      <Fragment>
        <Dialog
          className="sku-user-form-dialog"
          title={!editMode ? 'Add User' : 'Edit User'}
          icon={!editMode ? 'plus' : 'edit'}
          canOutsideClickClose={false}
          isOpen={true}
          onClose={onClose}
        >
          {this.renderForm()}
        </Dialog>

        {isOpenChangePasswordFormDialog && (
          <ChangePasswordFormDialog
            userData={userData}
            onClose={this.handleChangePasswordFormDialog}
          />
        )}
      </Fragment>
    );
  }
}

UserFormDialog.propTypes = {
  editMode: PropTypes.bool,
  userData: PropTypes.object.isRequired,
  getUsers: PropTypes.func.isRequired,
  onClose: PropTypes.func.isRequired
};

UserFormDialog.defaultProps = {
  editMode: false
};

export default UserFormDialog;
